/*
 * Copyright (C) 2024 pedroSG94.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.pedro.encoder.input.gl.render;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.os.Build;
import android.util.Log;
import android.view.Surface;

import androidx.annotation.RequiresApi;

import com.pedro.encoder.R;
import com.pedro.encoder.input.video.CameraHelper;
import com.pedro.encoder.utils.ViewPort;
import com.pedro.encoder.utils.gl.AspectRatioMode;
import com.pedro.encoder.utils.gl.GlUtil;
import com.pedro.encoder.utils.gl.SizeCalculator;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

/**
 * Created by pedro on 29/01/18.
 */

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class CameraRender extends BaseRenderOffScreen {

  private int[] textureID = new int[1];
  private float[] rotationMatrix = new float[16];
  private float[] scaleMatrix = new float[16];

  private int program = -1;
  private int uMVPMatrixHandle = -1;
  private int uSTMatrixHandle = -1;
  private int aPositionHandle = -1;
  private int aTextureCameraHandle = -1;

  private SurfaceTexture surfaceTexture;
  private Surface surface;

  public CameraRender() {
    Matrix.setIdentityM(MVPMatrix, 0);
    Matrix.setIdentityM(STMatrix, 0);
    float[] vertex = CameraHelper.getVerticesData();
    squareVertex = ByteBuffer.allocateDirect(vertex.length * FLOAT_SIZE_BYTES)
        .order(ByteOrder.nativeOrder())
        .asFloatBuffer();
    squareVertex.put(vertex).position(0);
    setRotation(0);
    setFlip(false, false);
  }

  @Override
  public void initGl(int width, int height, Context context, int previewWidth,
      int previewHeight) {
    this.width = width;
    this.height = height;
    GlUtil.checkGlError("initGl start");
    String vertexShader = GlUtil.getStringFromRaw(context, R.raw.simple_vertex);
    String fragmentShader = GlUtil.getStringFromRaw(context, R.raw.camera_fragment);

    program = GlUtil.createProgram(vertexShader, fragmentShader);
    aPositionHandle = GLES20.glGetAttribLocation(program, "aPosition");
    aTextureCameraHandle = GLES20.glGetAttribLocation(program, "aTextureCoord");
    uMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
    uSTMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix");
    uSTMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix");

    //camera texture
    GlUtil.createExternalTextures(textureID.length, textureID, 0);
    surfaceTexture = new SurfaceTexture(textureID[0]);
    surfaceTexture.setDefaultBufferSize(width, height);
    surface = new Surface(surfaceTexture);
    initFBO(width, height);
Log.d("testwidth","camera width:" + width + " height:" + height);
    GlUtil.checkGlError("initGl end");
  }

  @Override
  public void draw() {
    GlUtil.checkGlError("drawCamera start");
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderHandler.getFboId()[0]);

    surfaceTexture.getTransformMatrix(STMatrix);
    GLES20.glViewport(0, 0, width, height);
    GLES20.glUseProgram(program);
    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

    squareVertex.position(SQUARE_VERTEX_DATA_POS_OFFSET);
    GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,
        SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
    GLES20.glEnableVertexAttribArray(aPositionHandle);

    squareVertex.position(SQUARE_VERTEX_DATA_UV_OFFSET);
    GLES20.glVertexAttribPointer(aTextureCameraHandle, 2, GLES20.GL_FLOAT, false,
        SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
    GLES20.glEnableVertexAttribArray(aTextureCameraHandle);

    GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, MVPMatrix, 0);
    GLES20.glUniformMatrix4fv(uSTMatrixHandle, 1, false, STMatrix, 0);
    //camera
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureID[0]);
    //draw
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    GlUtil.checkGlError("drawCamera end");
  }

  public Bitmap getBitmap() {
    // 绑定到相机渲染的 FBO
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderHandler.getFboId()[0]);

    ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4);
    buffer.order(ByteOrder.nativeOrder());
    GLES20.glReadPixels(
            0, 0, width, height,
            GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer
    );

    Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    buffer.rewind();
    bmp.copyPixelsFromBuffer(buffer);

    // 解绑，恢复默认帧缓冲（屏幕）
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

//    // OpenGL 的坐标系原点在左下角，Android Bitmap 的原点在左上角，
//    // 所以需要垂直翻转一次
//    Matrix matrix = new Matrix();
////    matrix.preScale(1f, -1f);
//    return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, false);
    return bmp;
  }
  public Bitmap getBitmap(int targetWidth, int targetHeight) {
    ensureCaptureFbo(targetWidth, targetHeight);

    // 绑定截图用 FBO
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, captureFboId);
    GLES20.glViewport(0, 0, targetWidth, targetHeight);

    // 绘制一次到 captureFboId
    GLES20.glUseProgram(program);

    squareVertex.position(SQUARE_VERTEX_DATA_POS_OFFSET);
    GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,
            SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
    GLES20.glEnableVertexAttribArray(aPositionHandle);

    squareVertex.position(SQUARE_VERTEX_DATA_UV_OFFSET);
    GLES20.glVertexAttribPointer(aTextureCameraHandle, 2, GLES20.GL_FLOAT, false,
            SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
    GLES20.glEnableVertexAttribArray(aTextureCameraHandle);

    GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, MVPMatrix, 0);
    GLES20.glUniformMatrix4fv(uSTMatrixHandle, 1, false, STMatrix, 0);

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureID[0]);

    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    // 从 FBO 读像素
    ByteBuffer buffer = ByteBuffer.allocateDirect(targetWidth * targetHeight * 4);
    buffer.order(ByteOrder.nativeOrder());
    GLES20.glReadPixels(0, 0, targetWidth, targetHeight,
            GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);

    Bitmap bmp = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
    buffer.rewind();
    bmp.copyPixelsFromBuffer(buffer);

    // 翻转图像（因为 OpenGL Y 轴反的）
//    android.graphics.Matrix matrix = new android.graphics.Matrix();
//    matrix.preScale(1f, -1f);
//    bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, false);

    // 解绑
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

    return bmp;
  }

  /**
   * 获取 RGBA ByteBuffer，可直接传给 JNI
   * @param targetWidth 目标宽
   * @param targetHeight 目标高
   * @param rotation 旋转角度 (0 / 90 / 180 / 270)
   * @return 包含 RGBA 数据的 ByteBuffer (位置已 rewind，可直接 get())
   */
  public ByteBuffer getRgbaBuffer(int targetWidth, int targetHeight, int rotation) {
    ensureCaptureFbo(targetWidth, targetHeight);

    // 绑定截图用 FBO
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, captureFboId);
    GLES20.glViewport(0, 0, targetWidth, targetHeight);

    // 绘制一次到 captureFboId
    GLES20.glUseProgram(program);

    squareVertex.position(SQUARE_VERTEX_DATA_POS_OFFSET);
    GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,
            SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
    GLES20.glEnableVertexAttribArray(aPositionHandle);

    squareVertex.position(SQUARE_VERTEX_DATA_UV_OFFSET);
    GLES20.glVertexAttribPointer(aTextureCameraHandle, 2, GLES20.GL_FLOAT, false,
            SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex);
    GLES20.glEnableVertexAttribArray(aTextureCameraHandle);

    GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, MVPMatrix, 0);
    GLES20.glUniformMatrix4fv(uSTMatrixHandle, 1, false, STMatrix, 0);

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureID[0]);

    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    // 从 FBO 读像素 (RGBA8888)
    ByteBuffer buffer = ByteBuffer.allocateDirect(targetWidth * targetHeight * 4);
    buffer.order(ByteOrder.nativeOrder());
    GLES20.glReadPixels(0, 0, targetWidth, targetHeight,
            GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
    buffer.rewind();

//    // ---- 翻转 Y 轴（OpenGL 坐标系和 Android 不一样） ----
//    int[] temp = new int[targetWidth * targetHeight];
//    buffer.asIntBuffer().get(temp);

//    int[] flipped = new int[targetWidth * targetHeight];
//    for (int y = 0; y < targetHeight; y++) {
//      System.arraycopy(temp, y * targetWidth,
//              flipped, (targetHeight - 1 - y) * targetWidth,
//              targetWidth);
//    }
//
//    // ---- 根据 rotation 调整 ----
//    int[] rotated = (rotation == 0)
//            ? flipped
//            : rotateRgba(flipped, targetWidth, targetHeight, rotation);
//
//    // ---- 转回 ByteBuffer ----
//    ByteBuffer result = ByteBuffer.allocateDirect(rotated.length * 4);
//    result.order(ByteOrder.nativeOrder());
//    result.asIntBuffer().put(rotated);
//    result.rewind();

    // 解绑
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

    return buffer;
  }
  /**
   * 旋转 RGBA 数组
   * @param src 原始像素数组
   * @param width 原图宽
   * @param height 原图高
   * @param rotation 旋转角度 (支持 0, 90, 180, 270)
   */
  private int[] rotateRgba(int[] src, int width, int height, int rotation) {
    int[] dst;
    if (rotation == 90 || rotation == 270) {
      dst = new int[width * height];
    } else {
      dst = Arrays.copyOf(src, src.length);
    }

    switch (rotation) {
      case 90:
        for (int y = 0; y < height; y++) {
          for (int x = 0; x < width; x++) {
            dst[x * height + (height - y - 1)] = src[y * width + x];
          }
        }
        break;
      case 180:
        for (int i = 0; i < src.length; i++) {
          dst[src.length - 1 - i] = src[i];
        }
        break;
      case 270:
        for (int y = 0; y < height; y++) {
          for (int x = 0; x < width; x++) {
            dst[(width - x - 1) * height + y] = src[y * width + x];
          }
        }
        break;
    }
    return dst;
  }


  // --- 在 CameraRender 里添加成员变量 ---
  private int captureFboId = -1;
  private int captureTexId = -1;
  private int captureRboId = -1;
  private int captureWidth = 0;
  private int captureHeight = 0;

  // 创建或复用 FBO
  private void ensureCaptureFbo(int width, int height) {
    if (width == captureWidth && height == captureHeight && captureFboId != -1) {
      // 复用
      return;
    }
    releaseCaptureFbo(); // 删除旧的

    int[] fbo = new int[1];
    int[] tex = new int[1];
    int[] rbo = new int[1];

    GLES20.glGenFramebuffers(1, fbo, 0);
    GLES20.glGenTextures(1, tex, 0);
    GLES20.glGenRenderbuffers(1, rbo, 0);

    // 配置颜色纹理
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex[0]);
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
            width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

    // 配置深度缓冲
    GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, rbo[0]);
    GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,
            GLES20.GL_DEPTH_COMPONENT16, width, height);

    // 绑定到 FBO
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo[0]);
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
            GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, tex[0], 0);
    GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
            GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, rbo[0]);

    int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
    if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
      throw new RuntimeException("Framebuffer not complete, status=" + status);
    }

    // 恢复默认
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

    captureFboId = fbo[0];
    captureTexId = tex[0];
    captureRboId = rbo[0];
    captureWidth = width;
    captureHeight = height;
  }

  private void releaseCaptureFbo() {
    if (captureFboId != -1) {
      GLES20.glDeleteFramebuffers(1, new int[]{captureFboId}, 0);
      captureFboId = -1;
    }
    if (captureTexId != -1) {
      GLES20.glDeleteTextures(1, new int[]{captureTexId}, 0);
      captureTexId = -1;
    }
    if (captureRboId != -1) {
      GLES20.glDeleteRenderbuffers(1, new int[]{captureRboId}, 0);
      captureRboId = -1;
    }
    captureWidth = 0;
    captureHeight = 0;
  }


  @Override
  public void release() {
    GLES20.glDeleteProgram(program);
    surfaceTexture.release();
    surface.release();
  }

  public void updateTexImage() {
    surfaceTexture.updateTexImage();
  }

  public SurfaceTexture getSurfaceTexture() {
    return surfaceTexture;
  }

  public Surface getSurface() {
    return surface;
  }

  public void setRotation(int rotation) {
    Matrix.setIdentityM(rotationMatrix, 0);
    Matrix.rotateM(rotationMatrix, 0, rotation, 0f, 0f, -1f);
    update();
  }

  public void setFlip(boolean isFlipHorizontal, boolean isFlipVertical) {
    Matrix.setIdentityM(scaleMatrix, 0);
    Matrix.scaleM(scaleMatrix, 0, isFlipHorizontal ? -1f : 1f, isFlipVertical ? -1f : 1f, 1f);
    update();
  }

  private void update() {
    Matrix.setIdentityM(MVPMatrix, 0);
    Matrix.multiplyMM(MVPMatrix, 0, scaleMatrix, 0, MVPMatrix, 0);
    Matrix.multiplyMM(MVPMatrix, 0, rotationMatrix, 0, MVPMatrix, 0);
  }
}
